// $Id: component.cpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#define XPCOM_SOURCE

#include <xpcom/config.hpp>
#include <xpcom/assert.hpp>
#include <xpcom/component.hpp>
#include <xpcom/ptr.hpp>
#include <xpcom/result.hpp>
#include <xpcom/result_code.hpp>
#include "api/xpcom.hpp"
#include "nsIComponentManager.hpp"
#include "nsIServiceManager.hpp"

namespace xpcom {
namespace component {

namespace {

template <typename T>
struct ref_aux
{
    T* p;
    result r;
    bool release;

    template <typename Func>
    ref_aux(T* t, Func f) :
        p(t), release(false)
    {
        if (!p) {
            r = f(&p);
            if (r.succeeded()) {
                XPCOM_ASSERT(p);
                release = true;
            }
        }
    }

    ~ref_aux()
    {
        if (release) {
            XPCOM_ASSERT(p);
            p->release();
        }
    }
};

} // anonymous namespace

XPCOM_METHOD get_class_object::operator()(
        const uuid& iid, void** obj) const throw()
{
    ref_aux<nsIComponentManager> aux(manager_, ::NS_GetComponentManager);
    if (aux.r.failed())
        return aux.r;

    return aux.p->GetClassObject(clsid_, iid, obj);
}

XPCOM_METHOD get_class_object_by_contract::operator()(
        const uuid& iid, void** obj) const throw()
{
    // XPCOM will not check null pointer, so we check it!
    if (!contractid_)
        return NS_ERROR_NULL_POINTER;

    ref_aux<nsIComponentManager> aux(manager_, ::NS_GetComponentManager);
    if (aux.r.failed())
        return aux.r;

    return aux.p->GetClassObjectByContractID(contractid_, iid, obj);
}

XPCOM_METHOD create_instance::operator()(
        const uuid& iid, void** obj) const throw()
{
    ref_aux<nsIComponentManager> aux(manager_, ::NS_GetComponentManager);
    if (aux.r.failed())
        return aux.r;

    return aux.p->CreateInstance(clsid_, delegate_, iid, obj);
}

XPCOM_METHOD create_instance_by_contract::operator()(
        const uuid& iid, void** obj) const throw()
{
    // XPCOM will not check null pointer, so we check it!
    if (!contractid_)
        return NS_ERROR_NULL_POINTER;

    ref_aux<nsIComponentManager> aux(manager_, ::NS_GetComponentManager);
    if (aux.r.failed())
        return aux.r;

    return aux.p->CreateInstanceByContractID(contractid_, delegate_, iid, obj);
}

XPCOM_METHOD get_service::operator()(
        const uuid& iid, void** obj) const throw()
{
    ref_aux<nsIServiceManager> aux(manager_, ::NS_GetServiceManager);
    if (aux.r.failed())
        return aux.r;

    return aux.p->GetService(clsid_, iid, obj);
}

XPCOM_METHOD get_service_by_contract::operator()(
        const uuid& iid, void** obj) const throw()
{
    // XPCOM will not check null pointer, so we check it!
    if (!contractid_)
        return NS_ERROR_NULL_POINTER;

    ref_aux<nsIServiceManager> aux(manager_, ::NS_GetServiceManager);
    if (aux.r.failed())
        return aux.r;

    return aux.p->GetServiceByContractID(contractid_, iid, obj);
}

bool services_t::is_instantiated(
        const uuid& clsid, const uuid& iid,
        noref_ptr<nsIServiceManager> manager)
{
    ref_aux<nsIServiceManager> aux(manager.get(), ::NS_GetServiceManager);
    if (aux.r.failed())
        return false;

    // XXX result is not checked
    bool_t ret = false_;
    aux.p->IsServiceInstantiated(clsid, iid, &ret);
    return ret != false_;
}

bool services_t::is_instantiated(
        const char* contractid, const uuid& iid,
        noref_ptr<nsIServiceManager> manager)
{
    // XPCOM will not check null pointer, so we check it!
    if (!contractid)
        return false;

    ref_aux<nsIServiceManager> aux(manager.get(), ::NS_GetServiceManager);
    if (aux.r.failed())
        return false;

    // XXX result is not checked
    bool_t ret = false_;
    aux.p->IsServiceInstantiatedByContractID(contractid, iid, &ret);
    return ret != false_;
}

} // namespace xpcom::component
} // namespace xpcom
